/*! 
 * jquery.ux.popdown - v 1.0.0 - http://jquery-ux.com/
 * Copyright (c) 2010 Michael Helgeson, Three Dub Media
 * Open Source MIT License - http://jquery-ux.com/license 
 */
// Created: 2009-07-16
// Updated: 2010-01-22
// REQUIRES: jquery 1.4+, jquery.ux, jquery.align

;(function( $ ){ // confine scope

$.ux( "popdown", { // Register the PLUGIN... and public methods...
	version: "1.0.0",
	expose: "show hide keep", // + enable disable destroy
	defaults: {
		html: '<strong>Hello World</strong>',
		align: "outside outside",
		alignTo: null,
		showEvent: "focus", // {String}
		hideEvent: "blur", // {String}
		shareEvents: true, // {Boolean}
		delay: null,
		divStyle: { position:"absolute", top:0, left:0 },
		// classroot: "ux-popdown"
		// ux-popdown-div
		// ux-popdown-open
		},
	create: function( elem, opts ){
		// store args, target element 
		var self = $.extend( this, { 
			elem: elem, 
			opts: opts, 
			$elem: $( elem )
			}); 
		// bind events
		if ( opts.showEvent ) 
			this.$elem.bind( opts.showEvent, $.proxy( this, "handler") );
		if ( opts.hideEvent ) 
			this.$elem.bind( opts.hideEvent, $.proxy( this, "handler") );
		// support multiple space seperated events
		this.showRegExp = new RegExp("^"+( opts.showEvent || "" ).split(" ").join("|") +"$");
		this.hideRegExp = new RegExp("^"+( opts.hideEvent || "" ).split(" ").join("|") +"$");
		// bind callbacks
		/*
		if ( opts.onShow ) 
			this.$elem.bind("popdown:show", opts.onShow );
		if ( opts.onHide ) 
			this.$elem.bind("popdown:hide", opts.onHide );	
		*/
		},
	destroy: function(){
		this.hide();
		if ( this.$div ) 
			this.$div.find(':ux').ux("destroy").remove();
		if ( this.opts.showEvent ) 
			this.$elem.unbind( this.opts.showEvent, $.proxy( this, "handler") );
		if ( this.opts.hideEvent ) 
			this.$elem.unbind( this.opts.hideEvent, $.proxy( this, "handler") );	
		/*
		if ( this.opts.onShow ) 
			this.$elem.unbind("popdown:show", this.opts.onShow );
		if ( this.opts.onHide ) 
			this.$elem.unbind("popdown:hide", this.opts.onHide );		
		*/
		},
	methods: {
		// handle different events
		handler: function( ev ){
			if ( this.disabled ) return;	
			// handle show event
			if ( this.showRegExp.test( ev.type ) )
				// event target matches the orig element
				if ( ev.currentTarget == this.elem )
					this.timeout("show");
			// handle hide event	
			if ( this.hideRegExp.test( ev.type ) )
				this.timeout("hide", true );
			// handle document wide hide (mousedown)
			if ( ev.type == "mousedown" ){ 
				// event target matches the orig element
				if ( ev.currentTarget == this.elem )
					this.keep();
				// event is inside the popped div
				else if ( this.$div && $( ev.target ).closest("." + this.classify("div") )[0] == this.$div[0] )
					this.keep();			 
				// hide	
				else this.timeout("hide", true );
				}
			},
		// defered initialization of the content/container
		init: function( content ){
			// the container element
			this.$div = $('<div/>').addClass( this.classify("div") )
			if ( this.opts.divStyle ) 
				this.$div.css( this.opts.divStyle );
			// bind events
			if ( this.opts.shareEvents && this.opts.showEvent ) 
				this.$div.bind( this.opts.showEvent, $.proxy( this, "keep") );
			if ( this.opts.shareEvents &&  this.opts.hideEvent ) 
				this.$div.bind( this.opts.hideEvent, $.proxy( this, "handler") );
			// try to make IE6 happy...
			if ( this.$div.bgiframe ) this.$div.bgiframe();
			// fill the content
			this.show( content || this.opts.html );
			},
		// call a method inside a delayed timeout
		timeout: function( method, extra ){
			var self = this;
			// cancel the show/hide timer
			clearTimeout( this.timer );
			// set a new show/hide timer
			this.timer = setTimeout(function(){ 
				self[ method ]( extra ); 
				}, this.opts.delay || 0 );
			},
		// shows the popdown div
		show: function( content ){
			if ( this.disabled || this.isOpen ) return;
			// initialize the content on first show...
			if ( !this.$div ) return this.init.apply( this, arguments );
			// fill the div...
			if ( content ){
				if ( $.isFunction( content ) ) 
					content = content.call( this.elem, this.$div );
				this.$div.append( content || "" );	
				}
			// insert and prepare the container
			this.$div.stop( true, true ).hide().appendTo( document.body );
			// align the container
			if ( this.opts.align !== false )
				this.$div.align( this.opts.alignTo || this.elem, this.opts.align );
			// execute the callback
			this.publish("show", this.$div ); // popdown:show
			this.$elem.addClass( this.classify("open") );
			this.$div.show();
			this.isOpen = true;
			// bind document click/mousedown
			$( document ).bind("mousedown", $.proxy( this, "handler") );	
			},
		// keeps the popup open
		keep: function(){
			this.ignore = true;
			},
		// hides the popdown div
		hide: function( suppress ){ 
			if ( this.disabled || !this.$div ) return;
			// if hiding is suppresses
			if ( suppress && this.ignore ) return !( this.ignore = false );
			// execute the callback
			this.publish("hide", this.$div.stop(true,true) ); // popdown:hide
			this.$elem.removeClass( this.classify("open") );
			document.body.removeChild( this.$div.hide()[0] );
			this.isOpen = false;
			$( document ).unbind("mousedown", $.proxy( this, "handler") );
			}
		}
	});

})( jQuery ); // secure the $ jQuery alias